package com.wk.springboot.tree;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * description:
 * date 2021-01-03 14:22
 *
 * @author Administrator
 * @version 1.0
 */

@Slf4j
@Data
public class PathTree {

    private static volatile PathTree instance;

    private static Node root;

    private AtomicInteger id;

    private final ConcurrentMap<Integer, Node> NODE_CONCURRENT_MAP = new ConcurrentHashMap<>();

    private PathTree() {
        root = new Node("root");
        id = new AtomicInteger(0);
        int rootId = id.get();
        root.setId(rootId);
        root.setLevel(1);
        NODE_CONCURRENT_MAP.putIfAbsent(rootId, root);
    }

    public static PathTree getInstance() {
        if (instance == null) {
            synchronized (PathTree.class) {
                if (instance == null) {
                    instance = new PathTree();
                }
            }
        }
        return instance;
    }

    public List<Node> getNodeInfoByName(String nodeName){
        List<Node> nodes = new ArrayList<>();
        NODE_CONCURRENT_MAP.forEach((k,v)->{
            if (v.getVal().equals(nodeName)) {
                nodes.add(v);
            }
        });
        return nodes;
    }

    /**
     * 根据nodeName获得nodePath
     * @param nodeName
     * @return
     */
    public String getNodePath(String nodeName){
        List<Node> nodes = new ArrayList<>();
        NODE_CONCURRENT_MAP.forEach((k,v)->{
            if (v.getVal().equals(nodeName)) {
                nodes.add(v);
            }
        });

        if(nodes.size()==0){
            log.warn("node:{} not exist!!",nodeName);
            return null;
        }

        if (nodes.size()>1) {
            log.warn("node:{} has one more node,please choose which nodeId that you add",nodeName);
            log.info("{}",nodes);
            return null;
        }
        return getNodePath(nodes.get(0).getId());
    }

    /**
     * 得到指定node的路径
     *
     * @param nodeId
     * @return
     */
    public String getNodePath(Integer nodeId) {

        Node childNode = getNodeById(nodeId);
        if (childNode == null) {
            log.warn("can not find node by id:{}", nodeId);
            return null;
        }
        StringBuffer stringBuffer = new StringBuffer();
        List<String> paths = new ArrayList<>();
        Node parent = childNode;
        do {
            paths.add(parent.getVal());
            parent = parent.getParent();
        } while (!parent.isRoot());

        for (int i = 0, size = paths.size(); i < size; i++) {
            stringBuffer.append(paths.get(size - i - 1)).append(File.separator);
        }

        return stringBuffer.toString();
    }

    /**
     * 得到所有的节点
     *
     * @return
     */
    public String getAllNode() {
        final StringBuffer buffer = new StringBuffer();
        NODE_CONCURRENT_MAP.forEach((k, v) -> {
            buffer.append(v.toString()).append("\n");
        });
        return buffer.toString();
    }

    /**
     * 根据父节点名字插入子节点
     * @param childName
     * @param val
     */
    public void addParentNode(String childName,String val){
        List<Node> nodes = new ArrayList<>();
        NODE_CONCURRENT_MAP.forEach((k,v)->{
            if (v.getVal().equals(childName)) {
                nodes.add(v);
            }
        });

        if(nodes.size()==0){
            log.warn("child:{} not exist!!",childName);
            return;
        }

        if (nodes.size()>1) {
            log.warn("child:{} has one more node,please choose which childId that you add",childName);
            log.info("{}",nodes);
            return;
        }
        addParentNode(nodes.get(0).getId(),val);
    }

    /**
     * 插入子节点
     *
     * @param childId
     * @param val
     */
    public void addParentNode(Integer childId, String val) {

        if (childId == 0) {
            log.warn("id:{} is root, should not insert parent of it", childId);
            return;
        }

        Node childNode = getNodeById(childId);
        if (childNode == null) {
            log.warn("can not find node by id:{}", childId);
            return;
        }

        Node parentNode = childNode.getParent();
        List<Node> childs = parentNode.getChild();
        childs.remove(childNode);

        //判断不能有重复的名字
        for (Node node : childs) {
            if (node.getVal().equals(val)) {
                log.warn("val :{} is exist", val);
                return;
            }
        }

        List<Node> nodeChilds = new ArrayList<>();
        nodeChilds.add(childNode);

        int nodeId = id.incrementAndGet();
        Node node = new Node(NodeType.DIR, val, parentNode, nodeChilds);
        node.setId(nodeId);

        childNode.setParent(node);

        childs.add(node);

        NODE_CONCURRENT_MAP.put(nodeId, node);
    }

    /**
     * 根据子节点名字插入父节点
     * @param parentName
     * @param val
     */
    public void addChildNode(String parentName,String val){
        List<Node> nodes = new ArrayList<>();
        NODE_CONCURRENT_MAP.forEach((k,v)->{
            if (v.getVal().equals(parentName)) {
                nodes.add(v);
            }
        });

        if(nodes.size()==0){
            log.warn("parent:{} not exist!!",parentName);
            return;
        }

        if (nodes.size()>1) {
            log.warn("parent:{} has one more node,please choose which parentId that you add",parentName);
            log.info("{}",nodes);
            return;
        }
        addChildNode(nodes.get(0).getId(),val);
    }

    /**
     * 插入父节点
     *
     * @param parentId
     * @param val
     */
    public void addChildNode(Integer parentId, String val) {

        Node parentNode = getNodeById(parentId);
        if (parentNode == null) {
            log.error("can not find node by id:{}", parentId);
            return;
        }
        List<Node> childs = parentNode.getChild();
        if (childs == null) {
            childs = new ArrayList<>();
        }

        //判断不能有重复的名字
        for (Node node : childs) {
            if (node.getVal().equals(val)) {
                log.warn("val :{} is exist", val);
                return;
            }
        }

        int nodeId = id.incrementAndGet();
        Node node = new Node(val, parentNode);
        node.setId(nodeId);
        childs.add(node);
        parentNode.setChild(childs);
        if (!NodeType.DIR.equals(parentNode.getType())) {
            parentNode.setType(NodeType.DIR);
        }
        NODE_CONCURRENT_MAP.put(nodeId, node);

    }

    /**
     * 根据id找到节点
     *
     * @param id
     * @return
     */
    private Node getNodeById(Integer id) {
        return NODE_CONCURRENT_MAP.get(id);
    }

    /**
     * 打印树状结构
     */
    public void printPathTree(){
//        StringBuffer stringBuffer = new StringBuffer(root.getVal());
//        List<Node> childs = root.getChild();
//        for (Node child : childs) {
//            stringBuffer.append(printPathNode(child));
//        }
        String parentPath = "\n";
        String pathNode = printPathNode(root);
        log.info(pathNode);
    }

    /**
     * 打印nodePath
     * @param node
     */
    private String printPathNode(Node node) {
        int level = node.getLevel();
        StringBuffer stringBuffer = new StringBuffer("\n");
        for (int i = 0; i < level; i++) {
            stringBuffer.append("\t");
        }
        List<Node> child = node.getChild();
        String path = stringBuffer.append(node.getVal()).toString();
        if (child == null) {
            return path;
        }
        for (Node n : child) {
            n.setLevel(level+1);
            path += printPathNode(n);
        }
        return path;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    private final static class Node {

        //default file
        private PathTree.NodeType type = PathTree.NodeType.FILE;

        private int id;

        private int level;

        private String val;

        private Node parent;

        private List<Node> child;

        public Node(String val) {
            this.val = val;
        }

        public Node(String val, Node parent) {
            this.val = val;
            this.parent = parent;
        }

        public Node(NodeType type, String val, Node parent) {
            this.type = type;
            this.val = val;
            this.parent = parent;
        }

        public Node(NodeType type, String val, Node parent, List<Node> child) {
            this.type = type;
            this.val = val;
            this.parent = parent;
            this.child = child;
        }

        /**
         * 判断是否为root节点
         *
         * @return
         */
        public boolean isRoot() {
            if (this.getParent() == null) {
                return true;
            }
            return false;
        }

        @Override
        public String toString() {
            String parent = this.getParent()==null?null:this.getParent().getVal();
            return "Node{" +
                    "type=" + type +
                    ", id=" + id +
                    ", level=" + level +
                    ", val='" + val + '\'' +
                    ", parent=" + parent+
                    ", child=" + child+
                    '}';
        }
    }

    private enum NodeType {
        DIR, FILE;
    }

}
